﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Yz.Base;
using Yz.Core.Tools;

namespace Yz.Core.Extensions
{
    public static class EntityExtension
    {
        #region 使用description获得数据字典
        /// <summary>
        /// 获取数据库的数据字典
        /// </summary>
        /// <typeparam name="TdbContext"></typeparam>
        /// <param name="dbContext"></param>
        /// <returns></returns>
        private static DbDictionary GetDbDictionary<TdbContext>(this TdbContext dbContext) where TdbContext : DbContext
        {
            string dbName = "";
            var dbAttr = Attribute.GetCustomAttribute(dbContext.GetType(), typeof(DescriptionAttribute));
            if (dbAttr != null)
            {
                dbName = ((DescriptionAttribute)dbAttr).Description;
            }
            var tabs = dbContext.GetDbTabDictionary();
            return new DbDictionary()
            {
                DbName = dbName,
                Tabs = tabs
            };
        }
        /// <summary>
        /// 根据dbContext 获得数据表的信息，包括字段信息（字段名称，备注，类型【string类型的还有长度】，是否为空，是否主键外键）
        /// </summary>
        /// <typeparam name="TdbContext"></typeparam>
        /// <param name="dbContext"></param>
        /// <returns></returns>
        private static List<DbTabDictionary> GetDbTabDictionary<TdbContext>(this TdbContext dbContext) where TdbContext : DbContext
        {

            var properties = dbContext.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            List<DbTabDictionary> tabDics = new List<DbTabDictionary>();
            foreach (var item in properties)
            {
                var descAttr = Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute));
                if (descAttr == null)
                {
                    continue;
                }
                string remark = ((DescriptionAttribute)descAttr).Description;
                if (!string.IsNullOrEmpty(remark))
                {
                    var cols = item.GetValue(dbContext, null).GetType().GenericTypeArguments[0].GetDbColDictionaryByType();
                    tabDics.Add(new DbTabDictionary()
                    {
                        Name = item.Name,
                        Cols = cols,
                        Remark = remark
                    });
                }
            }
            return tabDics;
        }
        /// <summary>
        /// 根据dbModel的Type类型获得字段信息（字段名称，备注，类型【string类型的还有长度】，是否为空，是否主键外键）
        /// </summary>
        /// <param name="type"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        private static List<DbColDictionary> GetDbColDictionaryByType(this Type type, bool sort = false)
        {
            var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            List<DbColDictionary> colDics = new List<DbColDictionary>();
            foreach (var item in properties)
            {
                var descAttr = Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute));

                if (descAttr == null)
                {
                    continue;
                }
                int order = 0;  ///排序
                if (sort)///导出、导入时赋值为true
                {
                    var columnAttr = Attribute.GetCustomAttribute(item, typeof(ColumnAttribute));
                    if (columnAttr != null && ((ColumnAttribute)columnAttr).Order > 0)
                    {
                        order = ((ColumnAttribute)columnAttr).Order;
                    }
                    else
                    {
                        continue;
                    }
                }
                string comment = "";//批注
                var cmtAttr = Attribute.GetCustomAttribute(item, typeof(DisplayNameAttribute));
                if (cmtAttr != null)
                {
                    comment = ((DisplayNameAttribute)cmtAttr).DisplayName;
                }
                string remark = ((DescriptionAttribute)descAttr).Description;
                bool primarkKey = item.CustomAttributes.Where(p => p.AttributeType == typeof(KeyAttribute)).Any();
                var colType = item.PropertyType;
                int maxLength = 0;
                if (colType == typeof(string))
                {
                    var maxLenAttr = Attribute.GetCustomAttribute(item, typeof(MaxLengthAttribute));
                    if (maxLenAttr != null)
                    {
                        maxLength = ((MaxLengthAttribute)maxLenAttr).Length;
                    }
                }
                bool isNull = !item.CustomAttributes.Where(p => p.AttributeType == typeof(RequiredAttribute)).Any();///是否为空
                if (primarkKey)
                {
                    isNull = primarkKey;
                }
                if (!string.IsNullOrEmpty(remark))
                {
                    colDics.Add(new DbColDictionary()
                    {
                        Name = item.Name,
                        Type = colType,
                        Remark = remark,
                        MaxLength = maxLength,
                        Null = isNull,
                        Key = primarkKey ? "主键" : "",
                        Comment = comment,
                        Order = order
                    });
                }
            }
            return colDics;
        }
        /// <summary>
        /// 根据dbcontext 获得NPOIExcel对象（数据字典）
        /// </summary>
        /// <typeparam name="TdbContext"></typeparam>
        /// <param name="dbContext"></param>
        /// <returns></returns>
        public static NPOIExcel GetDbDicNPOIExcel<TdbContext>(this TdbContext dbContext) where TdbContext : DbContext
        {
            var dbDics = dbContext.GetDbDictionary();
            return dbDics.GetDbDicNPOIExcel();
        }
        #endregion

        #region 使用代码注释获得数据字典

        /// <summary>
        /// 根据dbModel的Type类型获得字段信息（字段名称，备注，类型【string类型的还有长度】，是否为空，是否主键外键）
        /// </summary>
        /// <param name="type"></param>
        /// <param name="dicModels"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        private static List<DbColDictionary> GetDbColDictionaryByType(this Type type, Dictionary<string, string> dicModels, bool sort = false)
        {
            var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            List<DbColDictionary> colDics = new List<DbColDictionary>();
            foreach (var item in properties)
            {
                if (item.PropertyType.Name == "ICollection`1")
                {
                    continue;
                }
                string key = item.DeclaringType.FullName + "." + item.Name;
                string remark = dicModels.Where(p => p.Key == key).Select(p => p.Value).FirstOrDefault();
                if (!string.IsNullOrEmpty(remark))
                {
                    int order = 0;  ///排序
                    if (sort)///导出、导入时赋值为true
                    {
                        var columnAttr = Attribute.GetCustomAttribute(item, typeof(ColumnAttribute));
                        if (columnAttr != null && ((ColumnAttribute)columnAttr).Order > 0)
                        {
                            order = ((ColumnAttribute)columnAttr).Order;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    string comment = "";//批注
                    var cmtAttr = Attribute.GetCustomAttribute(item, typeof(DisplayNameAttribute));
                    var cmtAttr1 = Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute));
                    if (cmtAttr != null)
                    {
                        comment = ((DisplayNameAttribute)cmtAttr).DisplayName;
                    }
                    else if (cmtAttr1 != null)
                    {
                        comment = ((DescriptionAttribute)cmtAttr1).Description;
                    }

                    bool primarkKey = item.CustomAttributes.Where(p => p.AttributeType == typeof(KeyAttribute)).Any();
                    var colType = item.PropertyType;
                    int maxLength = 0;
                    if (colType == typeof(string))
                    {
                        var maxLenAttr = Attribute.GetCustomAttribute(item, typeof(MaxLengthAttribute));
                        if (maxLenAttr != null)
                        {
                            maxLength = ((MaxLengthAttribute)maxLenAttr).Length;
                        }
                    }
                    bool isNull = !item.CustomAttributes.Where(p => p.AttributeType == typeof(RequiredAttribute)).Any();///是否为空
                    if (primarkKey)
                    {
                        isNull = false;
                    }
                    colDics.Add(new DbColDictionary()
                    {
                        Name = item.Name,
                        Type = colType,
                        Remark = remark,
                        MaxLength = maxLength,
                        Null = isNull,
                        Key = primarkKey ? "主键" : "",
                        Comment = comment,
                        Order = order
                    });
                }

            }
            return colDics;
        }

        /// <summary>
        /// 根据dbContext 获得数据表的信息，包括字段信息（字段名称，备注，类型【string类型的还有长度】，是否为空，是否主键外键）
        /// </summary>
        /// <typeparam name="TdbContext"></typeparam>
        /// <param name="dbContext"></param>
        /// <param name="dicModels">model的注释字典集合</param>
        /// <returns></returns>
        private static List<DbTabDictionary> GetDbTabDictionary<TdbContext>(this TdbContext dbContext, Dictionary<string, string> dicModels) where TdbContext : DbContext
        {
            var properties = dbContext.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
            List<DbTabDictionary> tabDics = new List<DbTabDictionary>();
            foreach (var item in properties)
            {
                if (!string.IsNullOrEmpty(item.Name))
                {
                    var typeList = item.GetValue(dbContext, null).GetType().GenericTypeArguments;
                    if (typeList != null && typeList.Count() > 0)
                    {
                        var tbType = typeList[0];
                        string tbKey = tbType.FullName;
                        if (!string.IsNullOrEmpty(tbKey))
                        {
                            tbKey = tbKey.Trim();
                            string remark = dicModels.Where(p => p.Key == tbKey).Select(p => p.Value).FirstOrDefault();
                            if (!string.IsNullOrEmpty(remark))
                            {
                                var cols = tbType.GetDbColDictionaryByType(dicModels);
                                tabDics.Add(new DbTabDictionary()
                                {
                                    Name = item.Name,
                                    Cols = cols,
                                    Remark = remark
                                });
                            }
                        }
                    }
                }
            }
            return tabDics;
        }

        /// <summary>
        /// 获取数据库的数据字典
        /// </summary>
        /// <typeparam name="TdbContext"></typeparam>
        /// <param name="dbContext"></param>
        /// <param name="xmlPath">model的xml路径</param>
        /// <returns></returns>
        private static DbDictionary GetDbDictionary<TdbContext>(this TdbContext dbContext, Dictionary<string, string> dicModels) where TdbContext : DbContext
        {
            string dbName = dicModels.Where(p => p.Key == dbContext.GetType().FullName).Select(p => p.Value).FirstOrDefault();
            var tabs = dbContext.GetDbTabDictionary(dicModels);
            return new DbDictionary()
            {
                DbName = dbName,
                Tabs = tabs
            };
        }

        /// <summary>
        /// 根据dbcontext 获得NPOIExcel对象（数据字典）
        /// </summary>
        /// <typeparam name="TdbContext"></typeparam>
        /// <param name="dbContext"></param>
        /// <param name="xmlPath"></param>
        /// <returns></returns>
        public static NPOIExcel GetDbDicNPOIExcel<TdbContext>(this TdbContext dbContext, string xmlPath) where TdbContext : DbContext
        {
            if (!System.IO.File.Exists(xmlPath))
            {
                return null;
            }
            XElement xElement = XElement.Load(xmlPath);
            if (xElement == null)
            {
                return null;
            }
            XElement xeMmbs = xElement.Element("members");
            if (xeMmbs == null || !xeMmbs.Elements().Any())
            {
                return null;
            }
            var xeMmbList = xeMmbs.Elements("member");
            var dicModels = new Dictionary<string, string>();
            dicModels.Add("Yz.Base.BaseEntity.Id", "主键");
            dicModels.Add("Yz.Base.UpdateEntity.UpdateDate", "更新时间");
            dicModels.Add("Yz.Base.BaseEntityKey.Id", "主键");
            dicModels.Add("Yz.Base.UpdateEntityKey.UpdateDate", "更新时间");
            dicModels.Add("Yz.Base.BaseEntityLong.Id", "主键");
            dicModels.Add("Yz.Base.UpdateEntityLong.UpdateDate", "更新时间");
            foreach (var item in xeMmbList)
            {
                string key = item.Attribute("name").Value;
                if (!string.IsNullOrEmpty(key))
                {
                    key = key.Replace("T:", "");
                    key = key.Replace("F:", "");
                    key = key.Replace("P:", "").Replace("\n", "").Trim();
                    if (!string.IsNullOrEmpty(key))
                    {
                        if (!dicModels.ContainsKey(key))
                        {
                            var summary = item.Element("summary");
                            if (summary != null)
                            {
                                string value = summary.Value;
                                if (!string.IsNullOrEmpty(value))
                                {
                                    value = value.Replace("\n", "").Trim();
                                    if (!string.IsNullOrEmpty(value))
                                    {
                                        dicModels.Add(key, value);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            var dbDics = dbContext.GetDbDictionary(dicModels);
            return dbDics.GetDbDicNPOIExcel();
        }
        #endregion

        #region 公共方法
        private static string GetDbTypeName(Type type, int len)
        {
            string tName = "";
            if (type == typeof(string))
            {
                tName = "nvarchar(MAX)";
                if (len > 0)
                {
                    tName = "nvarchar(" + len + ")";
                }
            }
            else if (type == typeof(DateTime))
            {
                tName = "datetime";
            }
            else if (type == typeof(bool))
            {
                tName = "bit";
            }
            else if (type == typeof(int) || type.BaseType == typeof(Enum))
            {
                tName = "int";
            }
            return tName;
        }
        /// <summary>
        /// 根据dbdic获得npoiExcel对象
        /// </summary>
        /// <param name="dbDics"></param>
        /// <returns></returns>
        private static NPOIExcel GetDbDicNPOIExcel(this DbDictionary dbDics)
        {
            var excel = new NPOIExcel()
            {
                NPOISheets = new List<NPOISheet>(),
                ExcelName = dbDics.DbName + "数据字典.xls"
            };

            #region 首页目录对应的datable 结构
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("No", typeof(string));
            dt1.Columns.Add("Name", typeof(string));
            dt1.Columns.Add("Remark", typeof(string));
            var cols1 = new List<NPOIColumn>()
            {
                new NPOIColumn(){ ColName="No",ColTitles=new List<string>(){ "序号" } },
                new NPOIColumn(){ ColName="Name",ColTitles=new List<string>(){ "表名" },ColWidth=10000},
                new NPOIColumn(){ ColName="Remark",ColTitles=new List<string>(){ "备注" } ,ColWidth=20000}
            };
            #endregion

            #region model对应的datatable 结构
            DataTable dt = new DataTable();
            dt.Columns.Add("No", typeof(string));
            dt.Columns.Add("Name", typeof(string));
            dt.Columns.Add("Type", typeof(string));
            dt.Columns.Add("Null", typeof(string));
            dt.Columns.Add("Key", typeof(string));
            dt.Columns.Add("Remark", typeof(string));
            var cols = new List<NPOIColumn>()
            {
                new NPOIColumn(){ ColName="No",ColTitles=new List<string>(){ "序号" } },
                new NPOIColumn(){ ColName="Name",ColTitles=new List<string>(){ "字段名称" } },
                new NPOIColumn(){ ColName="Type",ColTitles=new List<string>(){ "字段类型" } },
                new NPOIColumn(){ ColName="Null",ColTitles=new List<string>(){ "是否为空" } },
                new NPOIColumn(){ ColName="Key",ColTitles=new List<string>(){ "主外键" } },
                new NPOIColumn(){ ColName="Remark",ColTitles=new List<string>(){ "备注" } ,ColWidth=20000 }
            };
            #endregion

            #region 首页目录的超链接
            var hyperlinks1 = new List<NPOIHyperlink>();
            #endregion

            #region 循环dbModel
            int tabIndex = 1;
            foreach (var tab in dbDics.Tabs)
            {
                #region 首页目录数据源
                DataRow dr1 = dt1.NewRow();
                dr1["No"] = tabIndex;
                dr1["Name"] = tab.Name;
                dr1["Remark"] = tab.Remark;
                dt1.Rows.Add(dr1);
                hyperlinks1.Add(new NPOIHyperlink()
                {
                    Col = 2,
                    Row = tabIndex + 2,
                    Address = tab.Name
                });
                tabIndex++;
                #endregion

                var tmpDt = dt.Clone();
                int colIndex = 1;
                #region 循环dbModel的字段
                foreach (var col in tab.Cols)
                {
                    DataRow dr = tmpDt.NewRow();
                    dr["No"] = colIndex;
                    dr["Name"] = col.Name;
                    dr["Type"] = GetDbTypeName(col.Type, col.MaxLength);
                    dr["Null"] = col.Null ? "Y" : "N";
                    dr["Remark"] = col.Remark;
                    dr["Key"] = col.Key;
                    colIndex++;
                    tmpDt.Rows.Add(dr);
                }
                excel.NPOISheets.Add(new NPOISheet()
                {
                    Data = tmpDt,
                    SheetName = tab.Name,
                    Columns = cols,
                    TitleName = tab.Name + "（" + tab.Remark + "）",
                    EmptyRowCount = 1,
                    EmptyColCount = 1,
                    Hyperlinks = new List<NPOIHyperlink>()
                    {
                        new NPOIHyperlink(){
                            Col=0,
                            Row=1,
                            Address=dbDics.DbName,
                            Content="<<返回"
                        }
                    }
                });
                #endregion 
            }
            #endregion

            #region 首页目录sheet赋值
            excel.NPOISheets.Insert(0, new NPOISheet()
            {
                Data = dt1,
                SheetName = dbDics.DbName,
                Columns = cols1,
                TitleName = dbDics.DbName,
                EmptyRowCount = 1,
                EmptyColCount = 1,
                Hyperlinks = hyperlinks1
            });
            #endregion 

            return excel;
        }
        #endregion 

    }
}
